<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>继承</title>
</head>

<body>
  <script>
    //※※实现原型链继承
    function SuperType() {
      this.property = true;
    }
    SuperType.prototype.getSuperValue = function () {
      return this.property;
    }
    function SubType() {
      this.subproperty = false;
    }
    //继承SuperType,让子类的原型称为父类的实例
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function () {
      return this.subproperty;
    }
    let instance = new SubType();
    console.log(instance.getSuperValue()); //true

    //覆盖父类的方法
    SubType.prototype.getSuperValue = function () {
      return false;
    }
    let instance2 = new SubType();
    console.log(instance2.getSuperValue());//false
    //通过对象字面量添加方法,会切断子类和父类的联系:
    SubType.prototype = {
      getSubValue() {
        return this.subproperty;
      },
      someOtherMethod() {
        return false;
      }
    };
    let instance3 = new SubType();
    // console.log(instance3.getSuperValue()); //instance3.getSuperValue is not a function

    //演示原型链的问题
    function Father() {
      this.colors = ["red", "green", "blue"]
    }
    function Son() { }
    //继承Father
    Son.prototype = new Father();
    //相当于创建了Son.prototype.colors属性,Son的所有实例都会共享这个属性,因此对s1.colors.push("black"),
    //在s2上也会反映出来.
    let s1 = new Son();
    s1.colors.push("black");
    let s2 = new Son();
    console.log(s1.colors, s2.colors);//内容都一样


    //※※盗用构造函数继承,解决了原型链继承的问题.
    function F1(name) {
      this.name = name;
      this.colors = ['red', 'green', 'blue', 'pink'];
    }
    //继承F1,并向父类F1传递参数
    function S1() {
      F1.call(this, "Sloan");
      this.age = 22;
    }

    let s3 = new S1();
    s3.colors.push('black');
    let s4 = new S1();
    console.log(s3.colors, s4.colors, s3.name, s3.age); //"sloan",22


    //※※组合继承:原型链 + 盗用构造函数
    function F2(name) {
      this.name = name;
      this.colors = ['red', 'yellow', 'green'];
    }
    //父类原型方法
    F2.prototype.sayName = function () {
      console.log(this.name);
    }
    //继承属性
    function S2(name, age) {
      F2.call(this, name);
      this.age = age;
    }
    //继承方法
    S2.prototype = new F2();
    //子类方法
    S2.prototype.sayAge = function () {
      console.log(this.age);
    }
    //子类实例1
    let s21 = new S2('lishilong', 22);
    s21.colors.push('black');
    console.log(s21.colors);//['red', 'yellow', 'green', 'black']
    s21.sayName(); //lishilong, 继承了父类原型的方法
    s21.sayAge();//22 ,子类实例的方法
    //子类实例2
    let s22 = new S2('hhh', 23);
    console.log(s22.colors);// ['red', 'yellow', 'green']
    s22.sayName();//hhh
    s22.sayAge();//23


    //※※原型式继承
    function obj(o) {
      function F() { }
      F.prototype = o;
      return new F();
    }
    //Object.create()方法
    let person = {
      name: 'Sloan',
      friends: ['Angular', 'React', 'Vue']
    }
    let p2 = Object.create(person);
    p2.name = 'lisisi';
    p2.friends.push('jQuery');
    let p3 = Object.create(person);
    p3.name = 'wangwuwu';
    p3.friends.push('ES6');
    console.log(person.friends);//['Angular', 'React', 'Vue', 'jQuery', 'ES6']
    let p4 = Object.create(person, {
      name: {
        value: 'Grep'
      }
    });
    console.log(p4.name);//Grep , 覆盖父类的name.
    //※※寄生式继承
    function createAnother(obj) {
      let clone = Object.create(obj);
      clone.sayHi = function () {
        console.log('hi');
      };
      return clone;
    }
    let anotherPerson = createAnother(person);
    anotherPerson.sayHi();//hi
    //通过寄生式继承给对象添加函数会导致函数难以重用.

    //※※寄生式组合继承
    /* 
    inheritPrototype()函数实现了寄生式组合继承的核心逻辑,先创建父类构造函数的副本,
    然后给返回的prototype对象设置constructor属性,解决由于重写原型导致的默认的constructor
    丢失的问题,最后将新创建的对象赋值给子类型的原型.
    */
    function inheritPrototype(subType, superType) {
      let prototype = Object.create(superType.prototype);//父类原型的副本
      prototype.constructor = subType; //增强对象
      subType.prototype = prototype; //赋值对象
    }
    function F3(name) {
      this.name = name;
      this.colors = ['red', 'green', 'blue']
    }
    F3.prototype.sayName = function () {
      console.log(this.name);
    }
    function S3(name, age) {
      F3.call(this, name);
      this.age = age
    }
    inheritPrototype(S3, F3);
    S3.prototype.sayAge = function () {
      console.log(this.age);
    }
    let s31 = new S3('wen', 22);
    s31.colors.push('skyblue');
    let s32 = new S3('mei', 21);
    console.log(s31.colors, s32.colors); // ['red', 'green', 'blue', 'skyblue'] & ['red', 'green', 'blue']
    s32.sayName();//mei
    s32.sayAge(); //21
  </script>
</body>

</html>